#ifndef __PHLOGGER_WIN32_H__
#define __PHLOGGER_WIN32_H__

#pragma warning(disable: 4996)
#pragma warning(disable: 4995)

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <stdarg.h>
#include <time.h>

#ifndef _vscprintf
	#define _vscprintf(a,b) 10240
#endif //_vscprintf

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif 
#include <Windows.h>
#ifndef DISENABLE_LOGGER
    #include "Singleton_T.h"   
#endif
#ifdef _ENABLE_STACK_OUTPUT
    #include "debug/StackWalker.h"
#endif
#include "loggerdefine.h"
//#define _assert _ASSERT

#ifndef DISENABLE_LOGGER
  #ifdef _WRITE_LOG_TO_CONSOLE
	inline void OpenLogger(const char*){}
	inline void EnableLogger(bool){}
	inline void SetLoggerPipe(const char*){}
	inline void CloseLoggerPipe(){}    
	inline void SetLogPath(const char*){}
    inline void SetMaxSize(unsigned long) {}
	inline void SetLogFlag(int) {}
	inline void SetEnableRealTimeOutput(bool) {}

	static void WriteLog(int evt,const char* str,...)
	{
        if(!(evt & logConfigs)) return ;
        char stime[32] = "";
        time_t now = time(NULL);
        struct tm *tm = localtime(&(now));
        if (tm) strftime(stime, sizeof(stime),"%Y-%m-%d %H:%M:%S\t", tm);
            
		char *fmtstr = NULL;
		int len = 0;
		va_list   varg;   
		va_start(varg,   str);
		len = _vscprintf( str, varg );
		fmtstr = new char[len+1];
		fmtstr[len] = 0;
		vsnprintf(fmtstr,len, str,   varg);   
		va_end(varg); 
		printf("%s\t%s\t%s\n",stime, string_event(evt), fmtstr);
		delete [] fmtstr;
	}
  #else	//_WRITE_LOG_TO_CONSOLE
  
    typedef BOOL  (*WriteLogString_fn)(const LOG_FLAG evt, const char *file, const char *func, int line, const char* str);
    typedef BOOL  (*InitInstance_fn)();
    typedef BOOL  (*ExitInstance_fn)();
    typedef LOG_FLAG  (*SetOutputInfo_fn)(LOG_FLAG info);
    typedef LOG_FLAG  (*SetOutputMode_fn)(LOG_FLAG mode);
    typedef BOOL  (*SetAsynMode_fn)(BOOL bAsyn);
    typedef BOOL  (*SetLogPipeA_fn)(const char* pipe);
    typedef BOOL  (*CloseLogPipe_fn)();
    typedef BOOL  (*GetLogFileName_fn)(char* pipe ,LPDWORD size);
    typedef BOOL  (*SetLogPath_fn)(const char* szLogPath);
    typedef void  (*MaxSize_fn)(unsigned long size);
    typedef void  (*SetEnableRealTimeOutput_fn)(bool enabled);
    
	#define  LOGGER_INSTANCE CSingleton_T<CPHLogger>::Instance()
	class CPHLogger {
	public:
    #ifdef _ENABLE_STACK_OUTPUT
        struct StackOutput : public StackWalker
        {
        public:
            StackOutput() : StackWalker() {}
            StackOutput(DWORD dwProcessId, HANDLE hProcess) : StackWalker(dwProcessId, hProcess) {}
            virtual void OnOutput(LPCSTR szText) { 
                CSingleton_T<CPHLogger>::Instance().WriteLogString(LOG_DEBUG,"%s",szText); 
                StackWalker::OnOutput(szText); 
            }
        }m_stack_output;

    #endif
		~CPHLogger() {
			dwMagicWord = 0;
			m_bInit = FALSE;
			if(ExitInstanceFN) {
				ExitInstanceFN();
			}
			if(m_hLib) FreeLibrary(m_hLib);
		}

		BOOL Enable(BOOL bEnable) {
			m_bEnable = bEnable;
			return m_bEnable;
		}

		BOOL  WriteLogString(const LOG_FLAG evt, const char *file, const char *func, int line, const char* str,...) {
            if(!(evt & logConfigs)) return FALSE;
			if(!WriteLogStringFN || !m_bEnable) return FALSE;
			//_ASSERT(dwMagicWord == '@log');
			if(dwMagicWord == 0 )return FALSE;
			char *fmtstr = NULL;
			int len = 0;
			va_list   varg;   
			va_start(varg,   str);
			len = _vscprintf( str, varg );
			//_ASSERT(len == 4097);
			fmtstr = new char[len +1];
			fmtstr[len] = 0;
			_vsnprintf(fmtstr,len, str,   varg);   
			va_end(varg); 

			if(fmtstr == NULL ) return FALSE;
			BOOL b1 = WriteLogStringFN(evt, file, func, line, fmtstr);
			delete [] fmtstr;

			return b1;
		}
		LOG_FLAG  SetOutputInfo(LOG_FLAG info)
		{
			if(!SetOutputInfoFN ) return FALSE;
			//_ASSERT(dwMagicWord == '@log');
			if(dwMagicWord == 0 )return FALSE;
			return SetOutputInfoFN(info);
		}
		LOG_FLAG  SetOutputMode(LOG_FLAG mode)
		{
			if(!SetOutputModeFN ) return FALSE;
			//_ASSERT(dwMagicWord == '@log');
			if(dwMagicWord == 0 )return FALSE;
			return SetOutputModeFN(mode);
		}
		BOOL  SetAsynMode(BOOL bAsyn)
		{
			if(!SetAsynModeFN ) return FALSE;
			//_ASSERT(dwMagicWord == '@log');
			if(dwMagicWord == 0 )return FALSE;
			return SetAsynModeFN(bAsyn);
		}
		BOOL SetLogPipeA(const char* szFile)
		{
			if(!SetLogPipeAFN ) return FALSE;
			//_ASSERT(dwMagicWord == '@log');
			if(dwMagicWord == 0 )return FALSE;
			return SetLogPipeAFN(szFile);
		}
		BOOL CloseLogPipe()
		{
			if(!CloseLogPipeFN ) return FALSE;
			//_ASSERT(dwMagicWord == '@log');
			if(dwMagicWord == 0 )return FALSE;
			return CloseLogPipeFN();
		}
		BOOL GetLogFileNameA(char* szFile ,LPDWORD size)
		{
			if(!GetLogFileNameFn ) return FALSE;
			//_ASSERT(dwMagicWord == '@log');
			if(dwMagicWord == 0 )return FALSE;
			return GetLogFileNameFn(szFile,size);
		}

		BOOL SetLogPathA(const char* szLogPath)
		{
			if(!SetLogPathFn) return FALSE;
			//_ASSERT(dwMagicWord == '@log');
			if(dwMagicWord == 0 )return FALSE;
			return SetLogPathFn(szLogPath);
		}

        void MaxSize(unsigned long size)
        {
            if(!MaxSizeFn) return;
            return MaxSizeFn(size);
        }

		void SetEnableRealTimeOutput(bool enabled)
		{
			if(!SetEnableRealTimeOutputFn) return;
			return SetEnableRealTimeOutputFn(enabled);
		}

		WriteLogString_fn   WriteLogStringFN;
		InitInstance_fn     InitInstanceFN;
		ExitInstance_fn     ExitInstanceFN;
		SetOutputInfo_fn    SetOutputInfoFN;
		SetOutputMode_fn    SetOutputModeFN;
		SetAsynMode_fn      SetAsynModeFN;
		SetLogPipeA_fn      SetLogPipeAFN;
		CloseLogPipe_fn     CloseLogPipeFN;
		GetLogFileName_fn   GetLogFileNameFn;
		SetLogPath_fn       SetLogPathFn;
        MaxSize_fn          MaxSizeFn;
		SetEnableRealTimeOutput_fn SetEnableRealTimeOutputFn;

		CPHLogger()
			:m_hLib(NULL)
			,m_bInit(FALSE)
			,dwMagicWord('@log')
			,m_bEnable(TRUE)
			,SetLogPathFn(NULL)
		{
			WriteLogStringFN = NULL;
			InitInstanceFN= NULL;
			ExitInstanceFN= NULL;
			SetOutputInfoFN= NULL;
			SetOutputModeFN= NULL;
			SetAsynModeFN= NULL;
			SetLogPipeAFN= NULL;
			CloseLogPipeFN= NULL;
			GetLogFileNameFn=NULL;
            MaxSizeFn = NULL;
			SetEnableRealTimeOutputFn = NULL;

			if(LoadLib()){
				m_bInit = InitInstanceFN();
			}
		}

	private:
		//static CPHLogger s_Log;
		BOOL m_bEnable;
		
		//static CPHLogger log;
		BOOL m_bInit;
		HMODULE m_hLib;
		DWORD dwMagicWord;

		BOOL LoadLib()
		{
			if(m_hLib == NULL) {
				OutputDebugStringA("load oraylog.dll\n");
				m_hLib = LoadLibrary(_T("oraylog.dll"));
			}
			if(m_hLib == NULL)  return FALSE;

			WriteLogStringFN = (WriteLogString_fn) GetProcAddress(m_hLib,"WriteLogString");
			InitInstanceFN	 = (InitInstance_fn)   GetProcAddress(m_hLib,"InitInstance");
			ExitInstanceFN	 = (ExitInstance_fn)   GetProcAddress(m_hLib,"ExitInstance");
			SetOutputInfoFN	 = (SetOutputInfo_fn)  GetProcAddress(m_hLib,"SetOutputInfo");
			SetOutputModeFN	 = (SetOutputMode_fn)  GetProcAddress(m_hLib,"SetOutputMode");
			SetAsynModeFN	 = (SetAsynMode_fn)	   GetProcAddress(m_hLib,"SetAsynMode");
			SetLogPipeAFN	 = (SetLogPipeA_fn)	   GetProcAddress(m_hLib,"SetLogPipeA");
			CloseLogPipeFN	 = (CloseLogPipe_fn)   GetProcAddress(m_hLib,"CloseLogPipe");
			GetLogFileNameFn = (GetLogFileName_fn) GetProcAddress(m_hLib,"GetLogFileNameA");
			SetLogPathFn	 = (SetLogPath_fn)     GetProcAddress(m_hLib,"SetLogPathA");
            MaxSizeFn        = (MaxSize_fn)        GetProcAddress(m_hLib,"MaxSize");
			SetEnableRealTimeOutputFn = (SetEnableRealTimeOutput_fn) GetProcAddress(m_hLib,"SetEnableRealTimeOutput");

			return (WriteLogStringFN &&
					InitInstanceFN	&& 
					ExitInstanceFN	&& 
					SetOutputInfoFN && 
					SetOutputModeFN &&
					SetAsynModeFN);
		}
	};

	inline void OpenLogger(const char* name)
	{
		CSingleton_T<CPHLogger>::Instance().WriteLogString(LOG_DEBUG,"%s",name?name:"");
	}

	inline void SetLogPath(const char* szPath) {
		CSingleton_T<CPHLogger>::Instance().SetLogPathA(szPath);
	}

	inline void EnableLogger(bool b1)
	{
		CSingleton_T<CPHLogger>::Instance().Enable(b1);
	}

	inline BOOL SetLoggerPipe(const char* pipe){return CSingleton_T<CPHLogger>::Instance().SetLogPipeA(pipe);}
	inline BOOL CloseLoggerPipe(){return CSingleton_T<CPHLogger>::Instance().CloseLogPipe();}
	inline BOOL GetLoggerFileName(char * buf,LPDWORD size ) {return CSingleton_T<CPHLogger>::Instance().GetLogFileNameA(buf,size);}
    inline void SetMaxSize(unsigned long size) {return CSingleton_T<CPHLogger>::Instance().MaxSize(size);}
	inline void SetEnableRealTimeOutput(bool enabled) {return CSingleton_T<CPHLogger>Instance().SetEnableRealTimeOutput(enabled);}

    #ifdef _ENABLE_STACK_OUTPUT		
    #define WriteLog(...) do{\
        CSingleton_T<CPHLogger>::Instance().WriteLogString(__VA_ARGS__);\
            CSingleton_T<CPHLogger>::Instance().m_stack_output.ShowCallstack();\
        }while(0)
    #else
    #define WriteLog(evt, format, ...) do{ CSingleton_T<CPHLogger>::Instance().WriteLogString(evt, __FILE__, __FUNCTION__, __LINE__, format, __VA_ARGS__); \
		}while(0)
    #endif  //_ENABLE_STACK_OUTPUT

  #endif //_WRITE_LOG_TO_CONSOLE
#else //DISENABLE_LOGGER

	inline void OpenLogger(const char*){}
	inline void EnableLogger(bool){}
	inline void WriteLog(...){}
	inline void SetLoggerPipe(const char*){}
	inline void CloseLoggerPipe(){}
    inline void SetMaxSize(unsigned long) {}
	inline void SetEnableRealTimeOutput(bool) {}

#endif //DISENABLE_LOGGER

#endif //__PHLOGGER_H__

